package com.juc.concurrency.jucExample.singleton;

import com.juc.concurrency.annoations.ThreadSafe;

/**
 * 懒汉模式 -> 双重同步锁单例模式
 * 单例实例在第一次使用时进行创建
 * 线程安全：volatile关键字禁止指令重排！
 * 为什么第二次还要判断instance==null(line38):
 * 一开始当前线程A判断如果instance等于空，那么准备获得锁，
 * 但此时有可能另外一个线程B也在做这个操作，而且他先获得了锁。
 * 另一个线程B创建了instance对象，此时instance不再为空了。并释放了锁。
 * 这个时候原先的线程A终于获得了锁，但此时instance已经不是空了。
 * 如果你不判断instance是否还是为空，那么就会再次初始化对象。
 * 进一步解释：
 * 在极低的几率下，通过if (instance == null)的线程才会有进入锁定临界区的可能性，这种几率还是比较低的，不会阻塞太多的线程，
 * 但为了防止一个线程进入临界区创建实例，另外的线程也进去临界区创建实例，又加上了一道防御if (instance == NULL)，这样就确保不会重复创建了
 */
@ThreadSafe
public class SingletonExample5 {

    // 私有构造函数
    private SingletonExample5() {

    }

    // 1、memory = allocate() 分配对象的内存空间
    // 2、ctorInstance() 初始化对象
    // 3、instance = memory 设置instance指向刚分配的内存

    // 单例对象        volatile + 双重检测机制 -> 禁止指令重排
    private volatile static SingletonExample5 instance = null;

    // 静态的工厂方法
    public static SingletonExample5 getInstance() {
        if (instance == null) { // 双重检测机制        // B
            synchronized (SingletonExample5.class) { // 同步锁
                if (instance == null) {
                    instance = new SingletonExample5(); // A - 3
                }
            }
        }
        return instance;
    }
}
